﻿/**
 * 主程序，测试虚拟机的运行
 *
 * 创建时间：2022年5月21日
 * 作者：李述铜
 * 联系邮箱: 527676163@qq.com
 */
#include <stdio.h>
#include <stdlib.h>
#include "simu8051.h"
#include "simu8051_tools.h"

// nop, nop, sjmp $
static uint8_t t0_nop_code[] = {0x00, 0x00, 0x80, 0xFE};

#define REG_SP		0x1000
#define REG_A		0x1001
#define REG_B		0x1002
#define REG_PSW		0x1003
#define REG_PC		0x1004
#define REG_DPTR    0x1005
#define REG_CYCLE   0x1006
#define REG_R0		0x2000
#define REG_R1		0x2001
#define REG_R2		0x2002
#define REG_R3		0x2003
#define REG_R4		0x2004
#define REG_R5		0x2005
#define REG_R6		0x2006
#define REG_R7		0x2007
#define REG_END     0x2FFF

void check_result(void) {
    uint16_t addr = 0x600;
    do {
        // 读一个纪录的数据
        uint8_t addr0 = simu8051_read(MEM_TYPE_CODE, addr++);
        uint8_t addr1 = simu8051_read(MEM_TYPE_CODE, addr++);
        uint8_t data0 = simu8051_read(MEM_TYPE_CODE, addr++);
        uint8_t data1 = simu8051_read(MEM_TYPE_CODE, addr++);

        uint16_t cmp_addr = (addr0 << 8) | (addr1);     // 比较的地址
        uint16_t expect_data = (data0 << 8) | (data1);  // 期望的数据
        uint16_t real_data;         // 实际的数据

        switch (cmp_addr) {
        case REG_SP:
            real_data = simu8051_read(MEM_TYPE_SFR, SFR_SP);
            break;
        case REG_A:
            real_data = simu8051_read(MEM_TYPE_SFR, SFR_ACC);
            break;
        case REG_B:
            real_data = simu8051_read(MEM_TYPE_SFR, SFR_B);
            break;
        case REG_PSW:
            real_data = simu8051_read(MEM_TYPE_SFR, SFR_PSW);
            break;
        case REG_PC:
            real_data = simu8051_pc();
            break;
        case REG_DPTR:
            real_data = simu8051_read(MEM_TYPE_SFR, SFR_DPH) << 8;
            real_data |= simu8051_read(MEM_TYPE_SFR, SFR_DPL);
            break;
        case REG_CYCLE:
            real_data = simu8051_cycle();
            break;
        case REG_R0:  // 这里后面会考虑改成根据psw中配置分组读取
            real_data = simu8051_read(MEM_TYPE_IRAM, 0);
            break;
        case REG_R1:
            real_data = simu8051_read(MEM_TYPE_IRAM, 1);
            break;
        case REG_R2:
            real_data = simu8051_read(MEM_TYPE_IRAM, 2);
            break;
        case REG_R3:
            real_data = simu8051_read(MEM_TYPE_IRAM, 3);
            break;
        case REG_R4:
            real_data = simu8051_read(MEM_TYPE_IRAM, 4);
            break;
        case REG_R5:
            real_data = simu8051_read(MEM_TYPE_IRAM, 5);
            break;
        case REG_R6:
            real_data = simu8051_read(MEM_TYPE_IRAM, 6);
            break;
        case REG_R7:
            real_data = simu8051_read(MEM_TYPE_IRAM, 7);
            break;
        case REG_END:
            return;
        default:
            real_data = simu8051_read(MEM_TYPE_IRAM, cmp_addr);
            break;
        }

        if (real_data != expect_data) {
            printf("test error: addr = %x, real = %x, expect = %x\n", 
                cmp_addr, real_data, expect_data);
            exit(-1);
        }
    } while (1);
}

void test_memory()
{
    static uint8_t data[MEM_XRAM_SIZE];
    for (int i = 0; i < 128; i++) {
        simu8051_write(MEM_TYPE_BIT, i, 1);
        data[i] = simu8051_read(MEM_TYPE_BIT, i);
    }
    for (int i = 128; i < 256; i++) {
        simu8051_write(MEM_TYPE_BIT, i, 1);
        data[i] = simu8051_read(MEM_TYPE_BIT, i);
    }

    for (int i = 0; i < MEM_IRAM_SIZE; i++) {
        simu8051_write(MEM_TYPE_IRAM, i, i);
    }
    for (int i = 0; i < MEM_IRAM_SIZE; i++) {
        data[i] = simu8051_read(MEM_TYPE_IRAM, i);
    }

    for (int i = 0; i < MEM_XRAM_SIZE; i++) {
        simu8051_write(MEM_TYPE_XRAM, i, i);
        data[i] = simu8051_read(MEM_TYPE_XRAM, i);
    }

    simu8051_write(MEM_TYPE_SFR, SFR_ACC, 0x12);
    data[0] = simu8051_read(MEM_TYPE_SFR, SFR_ACC);
}

static const char * file_name[] = {
    "../../test/t1_simple/Objects/code.hex",
    //"asm_test/t2_move_0/Objects/code.hex",
    //"asm_test/t2_move_1/Objects/code.hex",
    //"asm_test/t2_move_2/Objects/code.hex",
    //"asm_test/t2_move_3/Objects/code.hex",
    //"asm_test/t3_movc_0/Objects/code.hex",
    //"asm_test/t5_push_pop/Objects/code.hex",
    //"asm_test/t5_push_pop/Objects/code.hex",
    //"asm_test/t6_xch/Objects/code.hex",
    //"asm_test/t7_anl/Objects/code.hex",
    //"asm_test/t8_orl/Objects/code.hex",
    //"asm_test/t9_xrl/Objects/code.hex",
    //"asm_test/t10_cpl_rr_rl/Objects/code.hex",
    //"asm_test/t11_add_0/Objects/code.hex",         // flag
    //"asm_test/t11_add_1/Objects/code.hex",
    //"asm_test/t12_inc/Objects/code.hex",
    //"asm_test/t13_dec/Objects/code.hex",
    //"asm_test/t14_subb_0/Objects/code.hex",
    //"asm_test/t14_subb_1/Objects/code.hex",
    //"asm_test/t15_mul_div_da/Objects/code.hex",
    //"asm_test/t16_jmp_call_ret/Objects/code.hex",
    //"asm_test/t17_djnz_jz_cjne/Objects/code.hex",
    //"asm_test/t18_bit_jb_jc/Objects/code.hex",
};

static void show_disa(instr_t * instr) {
    const instr_info_t * info = instr->info;

    printf("c:%2d\t a:%x\t", simu8051_cycle(), simu8051_pc());
    if (info->bytes == 1) {
        printf("o: %x\t i: %s", instr->opcode, info->disa);
    }
    else if (info->bytes == 2) {
        printf("o: %02x%02x\t i: %s", instr->opcode, instr->op0, info->disa);
    }
    else {
        printf("o: %02x%02x%02x\t i:%s", instr->opcode, instr->op0, instr->op1, info->disa);
    }

    putchar('\n');
}

void test_instr(void) {
    for (int i = 0; i < sizeof(file_name) / sizeof(const char *); i++) {
        uint16_t pc;
        uint8_t * code;

        printf("begin test: %s\n", file_name[i]);

        code = simu8051_load_hexfile(file_name[i]);
        if (code == NULL) {
            printf("load hex file failed: %s", file_name[i]);
            exit(-1);
        }

        simu8051_reset();
        simu8051_load(code);
        do {
            instr_t instr;

            pc = simu8051_pc();

            simu8051_fetch_instr(&instr);
            show_disa(&instr);

            simu8051_exec(&instr);
            simu8051_dump_regs();
        } while (pc != simu8051_pc());

        check_result();

        free(code);

        printf("test ok.\n");
    }
}

int main() {
    simu8051_init();
    test_instr();
    return 0;
}
